package com.ms.orm.meta;

import com.ms.common.util.StringUtils;
import com.ms.orm.annotation.*;
import com.ms.orm.config.GlobalConfig;
import com.ms.orm.constants.Constants;
import com.ms.orm.util.sql.JdbcType;
import com.ms.orm.util.sql.SqlScriptUtils;
import com.ms.orm.util.sql.TypeHandler;
import lombok.*;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author zdh
 * Java 属性 与 数据库字段映射
 */

@Data
@ToString
public class PropertyAndColumnInfo implements Serializable {


    /**
     * 是否是id
     */
    private boolean IdExit;
    /**
     * 是否映射到数据库
     */
    private boolean fieldExit;

    /**
     * 属性名
     */
    private String propertyName;
    /**
     * 属性类型
     */
    private Class javaType;

    /**
     * 数字库字段名称
     */
    private String columnName;

    /**
     * 数据库类型
     */
    private Integer jdbcType;


    private Field field;

    private Method propertyWriterMethod;

    private Method propertyReadMethod;




    /**
     * 字段验证策略之 insert
     * Refer to TableField#insertStrategy()
     * @since added v_3.1.2 @2019-5-7
     */
    private  FieldStrategy insertStrategy;
    /**
     * 字段验证策略之 update
     * Refer to TableField#updateStrategy()
     *
     * @since added v_3.1.2 @2019-5-7
     */
    private  FieldStrategy updateStrategy;
    /**
     * 字段验证策略之 where
     * Refer to TableField#whereStrategy()
     *
     * @since added v_3.1.2 @2019-5-7
     */
    private  FieldStrategy whereStrategy;

    /**
     * 是否是基本数据类型
     *
     * @since 3.4.0 @2020-6-19
     */
    private  boolean isPrimitive;
    /**
     * 属性是否是 CharSequence 类型
     */
    private  boolean isCharSequence;
    /**
     * 是否是乐观锁字段
     */
    private  boolean version;
    /**
     * 是否进行 select 查询
     * <p>大字段可设置为 false 不加入 select 查询范围</p>
     */
    private boolean select = true;
    /**
     * 是否是逻辑删除字段
     */
    @Getter
    private boolean logicDelete = false;
    /**
     * 逻辑删除值
     */
    private String logicDeleteValue;
    /**
     * 逻辑未删除值
     */
    private String logicNotDeleteValue;
    /**
     * 字段 update set 部分注入
     */
    private String update;
    /**
     * where 字段比较条件
     */
    private String condition = SqlCondition.EQUAL;

    /**
     * 字段填充策略
     */
    private FieldFill fieldFill = FieldFill.DEFAULT;

    /**
     * 表字段是否启用了插入填充
     */
    private boolean withInsertFill;

    /**
     * 表字段是否启用了更新填充
     */
    private boolean withUpdateFill;

    /**
     * 缓存 sql select
     */
    @Setter(AccessLevel.NONE)
    private String sqlSelect;

    /**
     * JDBC类型
     */
    private JdbcType jdbcTypeq;

    /**
     * 类型处理器
     */
    private Class<? extends TypeHandler<?>> typeHandler;

    /**
     * 是否存在OrderBy注解
     */
    private boolean isOrderBy;

    /**
     * 排序类型
     */
    private String orderByType;

    /**
     * 排序顺序
     */
    private short orderBySort;

    /**
     * 属性表达式#{property}, 可以指定jdbcType, typeHandler等
     */
    private  String el;


    /**
     * 排序初始化
     * @param field 字段
     */
    private void initOrderBy(Field field) {
        OrderBy orderBy = field.getAnnotation(OrderBy.class);
        if (null != orderBy) {
            this.isOrderBy = true;
            this.orderBySort = orderBy.sort();
            String _orderBy = Constants.DESC;
            if (orderBy.asc() || !orderBy.isDesc()) {
                _orderBy = Constants.ASC;
            }
            this.orderByType = _orderBy;
        } else {
            this.isOrderBy = false;
        }
    }

    /**
     * 逻辑删除初始化
     * @param dbConfig 数据库全局配置
     * @param field    字段属性对象
     */
    private void initLogicDelete(GlobalConfig.DbConfig dbConfig, Field field, boolean existTableLogic) {
        /* 获取注解属性，逻辑处理字段 */
        TableLogic tableLogic = field.getAnnotation(TableLogic.class);
        //待处理
    }

    /**
     * 获取 insert 时候插入值 sql 脚本片段
     * <p>insert into table (字段) values (值)</p>
     * <p>位于 "值" 部位</p>
     *
     * <li> 不生成 if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getInsertSqlProperty(final String prefix) {
        final String newPrefix = prefix == null ? Constants.EMPTY : prefix;
        return SqlScriptUtils.safeParam(newPrefix + el) + Constants.COMMA;
    }

    /**
     * 获取 insert 时候插入值 sql 脚本片段
     * <p>insert into table (字段) values (值)</p>
     * <p>位于 "值" 部位</p>
     *
     * <li> 根据规则会生成 if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getInsertSqlPropertyMaybeIf(final String prefix) {
        final String newPrefix = prefix == null ? Constants.EMPTY : prefix;
        String sqlScript = getInsertSqlProperty(newPrefix);
        if (withInsertFill) {
            return sqlScript;
        }
        return convertIf(sqlScript, newPrefix + propertyName, insertStrategy);
    }

    /**
     * 获取 insert 时候字段 sql 脚本片段
     * <p>insert into table (字段) values (值)</p>
     * <p>位于 "字段" 部位</p>
     *
     * <li> 不生成 if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getInsertSqlColumn() {
        return columnName + Constants.COMMA;
    }

    /**
     * 获取 insert 时候字段 sql 脚本片段
     * <p>insert into table (字段) values (值)</p>
     * <p>位于 "字段" 部位</p>
     *
     * <li> 根据规则会生成 if 标签 </li>
     *
     * @return sql 脚本片段
     */
    public String getInsertSqlColumnMaybeIf(final String prefix) {
        final String newPrefix = prefix == null ? Constants.EMPTY : prefix;
        final String sqlScript = getInsertSqlColumn();
        if (withInsertFill) {
            return sqlScript;
        }
        return convertIf(sqlScript, newPrefix + propertyName, insertStrategy);
    }

    /**
     * 获取 set sql 片段
     *
     * @param prefix 前缀
     * @return sql 脚本片段
     */
    public String getSqlSet(final String prefix) {
        return getSqlSet(false, prefix);
    }

    /**
     * 获取 set sql 片段
     *
     * @param ignoreIf 忽略 IF 包裹
     * @param prefix   前缀
     * @return sql 脚本片段
     */
    public String getSqlSet(final boolean ignoreIf, final String prefix) {
        final String newPrefix = prefix == null ? Constants.EMPTY : prefix;
        // 默认: column=
        String sqlSet = columnName + Constants.EQUALS;
        if (StringUtils.isNotBlank(update)) {
            sqlSet += String.format(update, columnName);
        } else {
            sqlSet += SqlScriptUtils.safeParam(newPrefix + el);
        }
        sqlSet += Constants.COMMA;
        if (ignoreIf) {
            return sqlSet;
        }
        if (withUpdateFill) {
            // 不进行 if 包裹
            return sqlSet;
        }
        return convertIf(sqlSet, convertIfProperty(newPrefix, propertyName), updateStrategy);
    }

    private String convertIfProperty(String prefix, String property) {
        return StringUtils.isNotBlank(prefix) ? prefix.substring(0, prefix.length() - 1) + "['" + property + "']" : property;
    }

    /**
     * 获取 查询的 sql 片段
     *
     * @param prefix 前缀
     * @return sql 脚本片段
     */
    public String getSqlWhere(final String prefix) {
        final String newPrefix = prefix == null ? Constants.EMPTY : prefix;
        // 默认:  AND column=#{prefix + el}
        String sqlScript = " AND " + String.format(condition, propertyName, newPrefix + el);
        // 查询的时候只判非空
        return convertIf(sqlScript, convertIfProperty(newPrefix, propertyName), whereStrategy);
    }


    /**
     * 转换成 if 标签的脚本片段
     *
     * @param sqlScript     sql 脚本片段
     * @param property      字段名
     * @param fieldStrategy 验证策略
     * @return if 脚本片段
     */
    private String convertIf(final String sqlScript, final String property, final FieldStrategy fieldStrategy) {
        if (fieldStrategy == FieldStrategy.NEVER) {
            return null;
        }
        if (isPrimitive || fieldStrategy == FieldStrategy.IGNORED) {
            return sqlScript;
        }
        if (fieldStrategy == FieldStrategy.NOT_EMPTY && isCharSequence) {
            return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and %s != ''", property, property),
                    false);
        }
        return SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", property), false);
    }

}
